package com.innovation.ic.b1b.monitor.base.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.innovation.ic.b1b.framework.util.HttpUtils;
import com.innovation.ic.b1b.framework.util.StringUtils;
import com.innovation.ic.b1b.monitor.base.mapper.ApiAvailableJobLogMapper;
import com.innovation.ic.b1b.monitor.base.mapper.ApiAvailableJobMapper;
import com.innovation.ic.b1b.monitor.base.mapper.ApiMapper;
import com.innovation.ic.b1b.monitor.base.model.Api;
import com.innovation.ic.b1b.monitor.base.model.ApiAvailableJob;
import com.innovation.ic.b1b.monitor.base.model.ApiAvailableJobLog;
import com.innovation.ic.b1b.monitor.base.pojo.constant.JobDataMapConstant;
import com.innovation.ic.b1b.monitor.base.pojo.enums.ApiAvailableEnum;
import com.innovation.ic.b1b.monitor.base.pojo.enums.ApiMethodEnum;
import com.innovation.ic.b1b.monitor.base.pojo.enums.ApiParameterTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import javax.annotation.Resource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.sql.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@Resource
public class ApiAvailableJobHandler extends QuartzJobBean {
    @Resource
    private ApiAvailableJobMapper apiAvailableJobMapper;
    @Resource
    private ApiAvailableJobLogMapper apiAvailableJobLogMapper;
    @Resource
    private ApiMapper apiMapper;
    @Resource
    private HandlerHelper handlerHelper;

    private static String downloadPath = "/opt";

    /**
     * 执行接口可用任务
     *
     * @param jobExecutionContext
     * @throws JobExecutionException
     */
    @Override
    protected void executeInternal(org.quartz.JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("执行接口可用任务 开始");
        JobDataMap jobDataMap = jobExecutionContext.getMergedJobDataMap();
        int id = jobDataMap.getInt(JobDataMapConstant.ID);
        ApiAvailableJob apiAvailableJob = apiAvailableJobMapper.selectById(id);
        if (apiAvailableJob == null) {
            log.info("执行接口可用任务 结束,id:{},没有符合条件接口任务", id);
            return;
        }
        Api api = apiMapper.selectById(apiAvailableJob.getApiId());
        try {

            //转换请求头
            Map<String, String> headers = handJsonToMap(api.getHeader());
            String result = null;

            //判断是否接口调用成功的标准。1表示正则表达式，2表示文件下载
            if (api.getResponseSuccessStandard() == 2) {
                Map<String, Object> map = DownAndReadFile(api.getUrl(), downloadPath);
                if ((boolean) map.get("downloadFlag")) {
                    InsertApiAvailableJobLog(apiAvailableJob, ApiAvailableEnum.AVAILABLE.getCode(), "接口名称:" + api.getName() + map.get("msg").toString());
                    return;
                }
                InsertApiAvailableJobLog(apiAvailableJob, ApiAvailableEnum.NO_AVAILABLE.getCode(), "接口名称:" + api.getName() + map.get("msg").toString());
                return;
            }

            //执行get请求
            if (ApiMethodEnum.GET.getCode().equals(api.getMethod())) {
                result = HttpUtils.doGet(api.getUrl(), null, headers);
            } else {
                //执行post请求
                if (ApiParameterTypeEnum.NULL.getCode().equals(api.getParameterType())) {
                    result = HttpUtils.doPostJson(api.getUrl(), "", headers, "");
                }

                if (ApiParameterTypeEnum.FROM.getCode().equals(api.getParameterType())) {
                    result = HttpUtils.doPostJson(api.getUrl(), api.getParameter(), headers, HttpUtils.APPLICATION_FORM_URLENCODED_VALUE);
                }

                if (ApiParameterTypeEnum.JSON.getCode().equals(api.getParameterType())) {
                    result = HttpUtils.doPostJson(api.getUrl(), api.getParameter(), headers, HttpUtils.APPLICATION_JSON_UTF8_VALUE);
                }
            }
            //返回空说调用接口失败
            if (StringUtils.isEmpty(result)) {
                InsertApiAvailableJobLog(apiAvailableJob, ApiAvailableEnum.NO_AVAILABLE.getCode(), "接口调用异常 : 接口名称:" + api.getName() + ",http请求失败");
                return;
            }

            //正则需要匹配响应
            if (StringUtils.isEmpty(api.getResponseSuccessRegularExpression())) {
                InsertApiAvailableJobLog(apiAvailableJob, ApiAvailableEnum.AVAILABLE.getCode(), "接口异常 : 接口名称:" + api.getName() + ",正则表达式为空，无法判断接口是否存活");
                return;
            }
            //处理正则
            Pattern r = Pattern.compile(api.getResponseSuccessRegularExpression());
            Matcher m = r.matcher(result);
            //判断正则
            if (m.matches()) {
                InsertApiAvailableJobLog(apiAvailableJob, ApiAvailableEnum.AVAILABLE.getCode(), "接口正常");
                return;
            }
            //调用返回接口失败
            InsertApiAvailableJobLog(apiAvailableJob, ApiAvailableEnum.NO_AVAILABLE.getCode(), "接口异常 : 接口名称:" + api.getName() + "," + result);
            return;
        } catch (Exception e) {
            log.info("执行接口可用任务 错误,e:{}", e);
            InsertApiAvailableJobLog(apiAvailableJob, ApiAvailableEnum.NO_AVAILABLE.getCode(), "执行接口可用任务 错误,接口名称:" + api.getName() + "," + e);
        }
    }

    /**
     * 请求头转换
     *
     * @param arr
     * @return
     */
    public static Map<String, String> handJsonToMap(String arr) {
        if (StringUtils.isEmpty(arr)) {
            return null;
        }
        Map<String, String> mapObj = JSON.parseArray(arr).stream().collect(
                Collectors.toMap(object -> {
                    JSONObject item = (JSONObject) object;
                    return item.getString("key");
                }, object -> {
                    JSONObject item = (JSONObject) object;
                    return item.getString("value");
                })
        );
        return mapObj;
    }


    /**
     * 插入接口可用日志
     *
     * @param apiAvailableJob
     * @param available
     */
    public void InsertApiAvailableJobLog(ApiAvailableJob apiAvailableJob, Integer available, String desc) {
        // 接口可用任务日志表
        ApiAvailableJobLog apiAvailableJobLog = new ApiAvailableJobLog();
        apiAvailableJobLog.setApiAvailableJobId(apiAvailableJob.getId());
        apiAvailableJobLog.setAvailable(available);
        apiAvailableJobLog.setStartTime(new Date(System.currentTimeMillis()));
        apiAvailableJobLog.setDescription(desc);
        int insert = apiAvailableJobLogMapper.insert(apiAvailableJobLog);
        if (insert > 0) {
            if (ApiAvailableEnum.NO_AVAILABLE.getCode().equals(available)) {
                handlerHelper.sendEmail(apiAvailableJob.getAlarmEmailId().toString(), desc, null);
            }
            log.info("执行接口可用任务 结束");
        }
    }


    /**
     * 下载文件
     *
     * @param urlStr   网络文件请求地址
     * @param savePath 保存地址
     */
    public static Map<String, Object> DownAndReadFile(String urlStr, String savePath) {
        Map<String, Object> map = new HashMap<>();
        // 声明文件对象
        File saverPath = new File(savePath);
        // 判断文件是否存在
        if (!saverPath.exists()) {
            // 文件不存在就创建一个一级目录【远程请求下载】
            saverPath.mkdir();
        }
        // 根据/切割接受到的请求网络URL
        String[] urlName = urlStr.split("/");
        // 获取到切割的字符串数组长度-1
        int len = urlName.length - 1;
        // 获取到请求下载文件的名称
        String uname = urlName[len];
        // 跳过try捕获错误
        try {
            // 创建保存文件对象
            File file = new File(saverPath + "/" + uname);//创建新文件
            if (file != null && !file.exists()) {
                file.createNewFile();
            }
            // 通过高效字节输出流输出创建的文件对象
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));
            // 创建URL对象[请求路径]
            URL url = new URL(urlStr);
            // 返回一个URLConnection实例，表示与URL引用的远程对象的URL
            HttpURLConnection uc = (HttpURLConnection) url.openConnection();
            // 设置的值 doInput领域本 URLConnection指定值。
            uc.setDoInput(true);
            // 打开与此URL引用的资源的通信链接，如果此类连接尚未建立。
            uc.connect();
            // 获取服务端的字节输入流
            InputStream inputStream = uc.getInputStream();
            // 声明字节数组存放读取的文件
            byte[] b = new byte[1024 * 4];
            int byteRead = -1; // 定义读取次数
            // 循环读取
            while ((byteRead = inputStream.read(b)) != -1) {
                // 将读取的文件跳过高效的字节流输出
                bufferedOutputStream.write(b, 0, byteRead);
            }
            // 关闭流和刷新流
            inputStream.close();
            bufferedOutputStream.close();
            //下载完成后删除
            file.delete();
        } catch (Exception e) {
            map.put("downloadFlag", false);
            map.put("msg", "下载接口异常:" + e.toString());
            return map;
        }
        map.put("downloadFlag", true);
        map.put("msg", "下载接口正常");
        return map;
    }
}
